package top.jle.spring.basic.aspect;

import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import top.jle.jpa.basic.exception.BasicCustomException;
import top.jle.jpa.basic.exception.BasicExceptionCode;
import top.jle.spring.basic.annotation.valid.ParameterValid;
import top.jle.spring.basic.annotation.valid.VerifyParam;
import top.jle.spring.basic.fis.MatcherExecutor;
import top.jle.spring.basic.fis.matchers.MatchGetter;
import java.lang.reflect.Method;
import java.util.Objects;

@Component
@Aspect
public class ParameterValidationAspect {

    private final static ExpressionParser parser = new SpelExpressionParser();
    @SuppressWarnings("unchecked")
    private void checkNotNull(VerifyParam verifyParam, Object arg){
        MatchGetter<BasicCustomException> notNullGetter = MatcherExecutor.matchAndGet(verifyParam.notNull().value());
        BasicCustomException exception = notNullGetter.get(()->{
            if(arg==null){
                return new BasicCustomException(BasicExceptionCode.INVALID_PARAM.getCode(),new JSONObject().fluentPut("message",verifyParam.notNull().message()));
            }
            return null;
        });
        if(exception!=null){
            throw exception;
        }
    }
    @SuppressWarnings("unchecked")
    private void checkNotBlank(VerifyParam verifyParam, Object arg){
        MatchGetter<BasicCustomException> notNullGetter = MatcherExecutor.matchAndGet(verifyParam.notBlank().value());
        BasicCustomException exception = notNullGetter.get(()->{
            if(Objects.toString(arg,"").isBlank()){
                return new BasicCustomException(BasicExceptionCode.INVALID_PARAM.getCode(),new JSONObject().fluentPut("message",verifyParam.notBlank().message()));
            }
            return null;
        });
        if(exception!=null){
            throw exception;
        }
    }
    @SuppressWarnings("unchecked")
    private void checkExpression(VerifyParam verifyParam, Object arg,EvaluationContext context){
        MatchGetter<BasicCustomException> notNullGetter = MatcherExecutor.matchAndGet(verifyParam.spEL().value());
        BasicCustomException exception = notNullGetter.get(()->{
            if(Objects.toString(arg,"").isBlank()){
                return new BasicCustomException(BasicExceptionCode.INVALID_PARAM.getCode(),new JSONObject().fluentPut("message",verifyParam.notBlank().message()));
            }else{
               Expression expression = parser.parseExpression(verifyParam.spEL().expression());
               Boolean result = expression.getValue(context,Boolean.class);
               if(!Boolean.TRUE.equals(result)){
                   return new BasicCustomException(BasicExceptionCode.INVALID_PARAM.getCode(),new JSONObject().fluentPut("message",verifyParam.spEL().message()));
               }
            }
            return null;
        });
        if(exception!=null){
            throw exception;
        }
    }
    @Before("@annotation(parameterValid)")
    public void parameterVerify(JoinPoint joinPoint, ParameterValid parameterValid) throws Throwable {
        VerifyParam[] verifyParams = parameterValid.value();
        if(ArrayUtils.isEmpty(verifyParams)){
            return;
        }
        Method method = getTargetMethod(joinPoint);
        EvaluationContext context = new MethodBasedEvaluationContext(new Object(),method,joinPoint.getArgs(),new DefaultParameterNameDiscoverer());
        Object[] args = parseExpression(joinPoint, verifyParams,context);
        for (int i = 0; i < args.length; i++) {
            VerifyParam verifyParam = verifyParams[i];
            Object arg = args[i];
            //校验NotNull
            checkNotNull(verifyParam, arg);
            //校验NotBlank
            checkNotBlank(verifyParam, arg);
            //校验表达式
            checkExpression(verifyParam,arg,context);
        }
    }
    /**
     * 解析达式获取方法参数
     * @param point 切点
     * @param verifyParams 验证参数
     * @param context 表达式上下文
     * @author jim.lee
     */
    private Object[] parseExpression(JoinPoint point, VerifyParam[] verifyParams,EvaluationContext context) throws NoSuchMethodException {
        Object[] res = new Object[verifyParams.length];
        for (int i = 0; i < verifyParams.length; i++) {
            Expression expression = parser.parseExpression(verifyParams[i].value());
            res[i]=expression.getValue(context);
        }
        return res;
    }
    private Method getTargetMethod(JoinPoint point) throws NoSuchMethodException {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method agentMethod = signature.getMethod();
        return point.getTarget().getClass().getMethod(agentMethod.getName(),agentMethod.getParameterTypes());
    }
}
